<?php

class DEngine_database_pdo extends DDatabase_engine  
{
		
	/**
	 * 连接数据库
	 * 
	 * @access public
	 * @param string $database
	 * @return object
	 */
	function connect($database = 'default')
	{
		parent::connect($database);
        $options = array(
            PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_ORACLE_NULLS  => PDO::NULL_EMPTY_STRING,
        );        
        $dsn = "{$this->_type}:host={$this->_host};port={$this->_port};dbname={$this->_name}";
        $instance = new DEngine_database_pdo_instance($dsn, $this->_user, $this->_pass, $options);        
		return $instance->encoding($this->_encoding)->setNames();
	}
	
}

//实例
class DEngine_database_pdo_instance extends PDO implements DIDatabase
{

    /**
     * 缓存代理
     *
     * @access public
     * @staticvar  
     * @var object
     */
    static $_cache_proxy = null;
    
    /**
    * 数据库取值方式
    *
    * @access private
    * @var string
    */
    private $_fetchMode = PDO::FETCH_ASSOC;
    
    /**
     * 支持的取值模式
     * 
     * @access private
     * @var array
     */
    private $_fetch_modes = array(
        DDatabase::FETCH_BOTH=> PDO::FETCH_BOTH,
        DDatabase::FETCH_ASSOC=> PDO::FETCH_ASSOC,
        DDatabase::FETCH_NUM=> PDO::FETCH_NUM,
    );
    
    /**
     * SQL语句执行后影响到的行数
     * 
     * @access private
     * @var integer
     */
    private static $_rowcount = 0;
    
    /**
     * 数据库编码
     * 
     * @access private
     * @var string
     */
    private $_encoding;
    
    /**
     * 构造器
     * 
     * @access public
     * @return null
     */
    function __construct($dsn, $user, $pass, $options = array()) 
    {
        try {
            parent::__construct($dsn, $user, $pass, $options);
        } catch(PDOException $e) {
            throw new DDatabaseException((D_DEBUG || D_MODE_CENSOR) ? $e->getMessage() : 'create_db_connection_failed');
        }
    }
    
    /**
     * 存/取数据库编码设置
     * 
     * @access public
     * @param string $encoding
     * @return mixed
     */
    function encoding($encoding = null) 
    {
        if (is_null($encoding)) {
            return $this->_encoding;
        }
        $this->_encoding = $encoding;
        return $this;
    }
    
    /**
     * 执行数据库编码设定
     * 
     * @access public
     * @return object
     */
    function setNames()
    {
        $this->query("set names '{$this->_encoding}'");
        return $this;
    }
    
    /**
     * 执行SQL语句并返回一个PDOStatement对象
     * 
     * @param string $sql
     * @return object
     */
    private function _query($sql) 
    {
        try {
            DTimer::Mark();
            is_string($sql) || $sql = (string)$sql;
            $smt = $this->prepare($sql);
            $smt->execute();
            $smt->setFetchMode($this->_fetchMode);
            self::$_rowcount = $smt->rowCount();
            DTimer::Mark();
            if (D_DEBUG || D_MODE_CENSOR) {
                DTrace::Set('database', array($smt->queryString, sprintf('%.4f', DTimer::Last())));
            }
            return $smt;
        } catch(PDOException $e) {
            if (DContext::Instance()->config->site->{D_HOST}->log->exception) { 
	            $log = PHP_EOL. date('Y-m-d H:i:s'). PHP_EOL. $smt->queryString. PHP_EOL. $e->getMessage(). PHP_EOL;
	            DContext::Instance()->logger->type('database')->name('exception')->save($log, true);
            }
            throw new DDatabaseException((D_DEBUG || D_MODE_CENSOR) ? $e->getMessage() : 'database_query_failed');
        }
    }
    
    /**
     * 返回所有数据
     * 
     * @access public
     * @param string $sql
     * @return mixed
     */
    function fetchAll($sql) 
    {
        return $this->_query($sql)->fetchAll($this->_fetchMode);
    }

    /**
     * 返回单行数据
     * 
     * @access public
     * @param string $sql
     * @return mixed
     */
    function fetch($sql) 
    {
        return $this->_query($sql)->fetch($this->_fetchMode);
    }
    
    /**
     * 返回第一行第一列数据，一般用在聚合函数中
     * 
     * @access public
     * @param string $sql
     * @return mixed
     */
    function fetchOne($sql)
    {
        return $this->_query($sql)->fetchColumn(0);
    }
    
    /**
     * 执行一个SQL语句
     * 
     * @access public
     * @param string $sql
     * @return int 
     */
    function query($sql) 
    {
        return $this->_query($sql)->rowCount();
    }
    
    /**
     * 返回上一次插入的数据ID
     * 
     * @access public
     * @param string $seq
     * @return integer
     */
    function lastIsertId($seq = null) 
    {
        return parent::lastInsertId($seq);
    }
    
    /**
     * 返回最近一次SQL语句执行后的影响行数
     * 
     * @access public
     * @return integer
     */
    function rowCount() 
    {
        return self::$_rowcount;
    }
    
    /**
     * 设置或获取当前的取值方式
     * 
     * @access public
     * @param string $fetchMode
     * @return string/void
     */
    function fetchMode($mode = null) 
    {
        if (is_null($mode)) {
            return $this->_fetchMode;
        } elseif (!isset($this->_fetch_modes[$mode])) {
            throw new DDatabaseException('mode_unsupported');
        }
        $this->_fetchMode = $this->_fetch_modes[$mode];
        return $this;
    }

    /**
     * 缓存代理
     * 
     * @access public
     * @param int $expire
     * @return object
     */
    function cache($expire = 86400)
    {
        if (is_null(self::$_cache_proxy)) {
            if (!class_exists('DCache', false)) {
                DContext::Instance()->cache;
            }
            self::$_cache_proxy = new DCache_proxy();
        }
        return self::$_cache_proxy->proxy($this, $expire);
    }
    
    /**
     * select助手
     * 
     * @access public
     * @return object
     */
    function select() 
    {
    	return DContext::Instance()->helper->database_select;
    }
    
    /**
     * delete助手
     * 
     * @access public
     * @return object
     */
    function delete()
    {
    	return DContext::Instance()->helper->database_delete;
    }
    
    /**
     * insert助手
     * 
     * @access public
     * @return object
     */
    function insert()
    {
    	return DContext::Instance()->helper->database_insert;
    }
    
    /**
     * update助手
     * 
     * @access public
     * @return object
     */
    function update()
    {
    	return DContext::Instance()->helper->database_update;
    }

}

?>